package org.dalmatian.common.core.utils.file;

import static org.dalmatian.common.core.enums.FileContentTypeEnum.EXCEL_XLS;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.EXCEL_XLSX;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.IMAGE_JPEG;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.IMAGE_JPG;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.IMAGE_PNG;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.PDF;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.POWERPOINT_PPT;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.POWERPOINT_PPTX;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.TEXT;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.WORD_DOC;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.WORD_DOCX;
import static org.dalmatian.common.core.enums.FileContentTypeEnum.ZIP;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.noear.solon.core.handle.UploadedFile;

/**
 * 文件处理工具类
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils extends FileUtil {

    private static final Pattern URL_PATTERN = Pattern.compile("^(http|https)://.*");
    private static final Pattern BASE64_PATTERN = Pattern.compile("^[A-Za-z0-9+/]+={0,2}$");
    private static final Pattern BASE64_WITH_PREFIX_PATTERN = Pattern.compile(
        "^data:image/.+;base64,.*");

    /**
     * 下载文件名重新编码
     *
     * @param response     响应对象
     * @param realFileName 真实文件名
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response,
        String realFileName) {
        String percentEncodedFileName = percentEncode(realFileName);
        String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(
            percentEncodedFileName, percentEncodedFileName);
        response.addHeader("Access-Control-Expose-Headers",
            "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue);
        response.setHeader("download-filename", percentEncodedFileName);
    }

    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    public static String percentEncode(String s) {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
        return encode.replaceAll("\\+", "%20");
    }

    /**
     * MultipartFile转为File类型方法
     *
     * @param multipartFile
     * @return 转换后的File文件
     */
    public static File convert(UploadedFile multipartFile) {
        try {
            // 创建临时文件
            String mainName = FileUtil.mainName(multipartFile.getName());
            if (mainName.length() < 3) {//文件长度小于3设置追加字符，避免创建临时文件因文件名长度小于3报错;
                mainName = mainName + "Temp";
            }
            File tempFile = File.createTempFile(mainName,
                "." + FileUtil.extName(multipartFile.getName()));

            InputStream inputStream = multipartFile.getContent();
            OutputStream outputStream = FileUtil.getOutputStream(tempFile);
            // 使用 Hutool 将输入流拷贝到临时文件中
            IoUtil.copy(inputStream, outputStream);
            return tempFile;
        } catch (IOException e) {
            throw new RuntimeException("Failed to convert MultipartFile to File");
        }
    }

    /**
     * 判断文件类型方法 仅支持以下类型: Excel (.xls, .xlsx) Word (.doc, .docx) PowerPoint (.ppt, .pptx) PDF (.pdf)
     * Text (.txt) ZIP (.zip)
     *
     * @param multipartFile
     * @return 类型校验
     */
    public static Boolean verifyFileType(UploadedFile multipartFile) throws IOException {
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new IOException("上传文件无有效内容,不可上传空白文件");
        }
        boolean flag = false;
        File file = convert(multipartFile);
        String extension = FileNameUtil.extName(file).toLowerCase();
        if (EXCEL_XLS.getName().equals(extension) ||       // Excel
            EXCEL_XLSX.getName().equals(extension) ||
            WORD_DOC.getName().equals(extension) ||
            WORD_DOCX.getName().equals(extension) ||
            POWERPOINT_PPT.getName().equals(extension) ||
            POWERPOINT_PPTX.getName().equals(extension) ||
            PDF.getName().equals(extension) ||
            TEXT.getName().equals(extension) ||
            ZIP.getName().equals(extension)) {
            flag = true;
        }
        return flag;
    }

    /**
     * 判断文件类型方法只支持Excel和Word格式
     *
     * @param multipartFile
     * @return
     */
    public static Boolean verifyFileExcelAndWordType(UploadedFile multipartFile)
        throws IOException {
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new IOException("上传文件无有效内容,不可上传空白文件");
        }
        boolean flag = false;
        File file = convert(multipartFile);
        String extension = FileNameUtil.extName(file).toLowerCase();
        // 只支持 Word 和 Excel 格式
        if (EXCEL_XLS.getName().equals(extension) ||
            EXCEL_XLSX.getName().equals(extension) ||
            WORD_DOC.getName().equals(extension) ||
            WORD_DOCX.getName().equals(extension)) {
            flag = true;
        }
        return flag;
    }

    /**
     * 判断公告咨询上传文件类型方法 仅支持以下类型: Excel (.xls, .xlsx) Word (.doc, .docx) PowerPoint (.ppt, .pptx) ZIP
     * (.zip) PDF (.pdf)
     *
     * @param multipartFile
     * @return 类型校验
     */
    public static Boolean verifyNoticeBoardFileType(UploadedFile multipartFile) {
        boolean flag = false;
        File file = convert(multipartFile);
        String extension = FileNameUtil.extName(file).toLowerCase();
        if (EXCEL_XLS.getName().equals(extension) ||
            EXCEL_XLSX.getName().equals(extension) ||
            WORD_DOC.getName().equals(extension) ||
            WORD_DOCX.getName().equals(extension) ||
            POWERPOINT_PPT.getName().equals(extension) ||
            POWERPOINT_PPTX.getName().equals(extension) ||
            PDF.getName().equals(extension)) {       // ZIP
            flag = true;
        }
        return flag;
    }

    /**
     * 判断图片类型方法 仅支持以下类型: (.png) (.jpg) (.jpeg)
     *
     * @param multipartFile
     * @return 类型校验
     */
    public static Boolean verifyFileImageType(UploadedFile multipartFile) {
        boolean flag = false;
        File file = convert(multipartFile);
        String extension = FileNameUtil.extName(file).toLowerCase();
        if (IMAGE_JPG.getName().equals(extension) ||
            IMAGE_JPEG.getName().equals(extension) ||
            IMAGE_PNG.getName().equals(extension)) {
            flag = true;
        }
        return flag;
    }

    /**
     * 根据url下载为byte字节流
     *
     * @param fileUrl
     * @return
     */
    public static byte[] downloadFileFromOssUrl(String fileUrl) {
        try {
            // 创建 URL 对象
            URL url = new URL(fileUrl);
            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            // 检查响应码
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                // 读取文件内容到字节数组
                try (InputStream inputStream = connection.getInputStream();
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                    return outputStream.toByteArray();
                }
            } else {
                // 处理下载失败的情况
                System.err.println("Failed to download file from URL: " + fileUrl);
                return null;
            }
        } catch (IOException e) {
            // 处理异常
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据url下载文件为临时文件
     *
     * @param templateUrl
     * @return
     * @throws IOException
     */
    public static File downloadPdfTemplate(String templateUrl) {
        try {
            URL url = new URL(templateUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            // 创建临时文件
            File tempFile = File.createTempFile("template-", ".pdf");
            try (InputStream in = connection.getInputStream();
                FileOutputStream out = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            }
            return tempFile;
        } catch (IOException e) {
            throw new RuntimeException("根据url创建临时文件失败");
        }
    }

    /**
     * 从 URL 下载图片并转换为 Base64 编码
     */
    public static String downloadImageAndConvertToBase64(String imageUrl) {
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();

            try (BufferedInputStream inputStream = new BufferedInputStream(
                connection.getInputStream());
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                // 获取字节数组并转换为 Base64
                byte[] imageBytes = outputStream.toByteArray();
                return Base64.getEncoder().encodeToString(imageBytes);
            }
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 将File文件转换为Base64编码
     */
    public static String convertFileToBase64(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            // 获取字节数组并转换为 Base64
            byte[] fileBytes = outputStream.toByteArray();
            return Base64.getEncoder().encodeToString(fileBytes);
        } catch (IOException e) {
            return null; // 可以考虑记录日志或抛出异常
        }
    }

    public static String imageToBase64(String image) throws IOException {
        // 如果是图片 URL 地址就转化为 Base64 编码
        if (isValidUrl(image)) {
            return downloadImageAndConvertToBase64(image);
        } else if (isBase64Image(image)) { // 如果是有效的 Base64 编码
            return image;
        } else if (isValidBase64WithoutPrefix(image)) { // 如果是有效的 Base64 编码，但没有前缀
            // 根据需要为其添加前缀，这里假设是 JPEG 格式
            return image; // 根据实际情况修改格式
        } else {
            throw new IOException("图片格式不正确");
        }
    }

    //检测图片地址url
    private static boolean isValidUrl(String url) {
        return URL_PATTERN.matcher(url).matches();
    }

    //检测有效 Base64 编码
    private static boolean isBase64Image(String base64) {
        return BASE64_WITH_PREFIX_PATTERN.matcher(base64).matches();
    }

    // 检测没有前缀的有效 Base64 编码
    private static boolean isValidBase64WithoutPrefix(String base64) {
        return BASE64_PATTERN.matcher(base64).matches();
    }
}
